"""
@author: 景云鹏
@email: 310491287@qq.com
@date: 2022/5/2
"""
import scipy.special
from numpy import *


class NN:
    def __init__(self, i, h, o, rate):
        self.i = i
        self.h = h
        self.o = o

        self.wih = random.normal(0.0, power(self.h, -0.5), (self.h, self.i))
        self.who = random.normal(0.0, power(self.o, -0.5), (self.o, self.h))
        self.rate = rate

        self.activate_function = scipy.special.expit

    def _flow(self, inputs):
        hi = dot(self.wih, inputs)
        ho = self.activate_function(hi)
        fi = dot(self.who, ho)
        fo = self.activate_function(fi)
        return ho, fo

    def train(self, input_list, target_list):
        inputs = array(input_list, ndmin=2).T
        targets = array(target_list, ndmin=2).T

        hidden_outputs, final_outputs = self._flow(inputs)

        output_errors = targets - final_outputs
        hidden_error = dot(self.who.T, output_errors)

        self.who += self.rate * dot(
            output_errors * final_outputs * (1 - final_outputs),
            transpose(hidden_outputs)
        )
        self.wih += self.rate * dot(
            hidden_error * hidden_outputs * (1 - hidden_outputs),
            transpose(inputs)
        )

    def query(self, inputs_list):
        inputs = array(inputs_list, ndmin=2).T
        return self._flow(inputs)[-1]

    def predict(self, inputs):
        return argmax(self.query(inputs))


def train(nn):
    with open('train.csv', 'r') as f:
        for line in f:
            values = line.split(',')
            inputs = asfarray(values[1:]) / 255 * 0.99 + 0.01
            targets = zeros(10) + 0.01
            targets[int(values[0])] = 0.99
            nn.train(inputs, targets)


def test(nn, filename):
    rd = zeros(100, dtype='int').reshape((10, 10))
    with open(filename, 'r') as f:
        for line in f:
            values = line.split(',')
            inputs = asfarray(values[1:]) / 255 * 0.99 + 0.01
            p = nn.predict(inputs)
            t = int(values[0])
            rd[t][p] += 1

    print_result(rd)

    return rd


def show_image(inputs):
    from matplotlib.pyplot import imshow, show
    imshow(
        asfarray(inputs).reshape((28, 28)),
        cmap='Greys',
        interpolation='None'
    )
    show()


def print_result(d):
    rows = zeros(10)
    cols = zeros(10)
    corrects = zeros(10)
    total = 0
    for i in range(10):
        for j in range(10):
            c = d[i][j]
            rows[i] += c
            cols[j] += c
            total += c
            if i == j:
                corrects[i] += c
    print(total)
    print('corrects', sum(corrects) / total)
    print(corrects / rows)
    print(corrects / cols)
    print()
    print()


if __name__ == '__main__':
    n = NN(784, 100, 10, 0.3)
    train(n)
    d1 = test(n, 'train.csv')
    d2 = test(n, 'test.csv')

    # print(values[0])

    # break
